home *** CD-ROM | disk | FTP | other *** search
/ Atari Mega Archive 1 / Atari Mega Archive - Volume 1.iso / program / cpp112.zoo / src / token.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-07-07  |  17.6 KB  |  794 lines

  1.  
  2. /*---------------------------------------------------------------------*\
  3. |                                    |
  4. | CPP -- a stand-alone C preprocessor                    |
  5. | Copyright (c) 1993 Hacker Ltd.        Author: Scott Bigham    |
  6. |                                    |
  7. | Permission is granted to anyone to use this software for any purpose    |
  8. | on any computer system, and to redistribute it freely, with the    |
  9. | following restrictions:                        |
  10. | - No charge may be made other than reasonable charges for repro-    |
  11. |     duction.                                |
  12. | - Modified versions must be clearly marked as such.            |
  13. | - The author is not responsible for any harmful consequences of    |
  14. |     using this software, even if they result from defects therein.    |
  15. |                                    |
  16. | token.c -- transform raw input to preprocessor tokens            |
  17. \*---------------------------------------------------------------------*/
  18.  
  19. /*
  20.    There are (for better or worse) three interfaces to the tokenizing
  21.    mechanism, at increasing levels of abstraction.  _one_token() tokenizes
  22.    directly out of the input line buffer, and should generally only be used
  23.    while handling preprocessor directive lines.  token() pre-tokenizes an
  24.    entire line of input at a time and doles it out one token at a time.  Note
  25.    that tokens returned by token() may or may not have undergone macro
  26.    expansion; use it when you need access to pre-expanded input tokens.
  27.    exp_token() fully expands each token before returning it; this is usually
  28.    the best way to access input.
  29. */
  30.  
  31. #include <ctype.h>
  32. #include <stdlib.h>
  33. #include <stdio.h>
  34. #include <stddef.h>
  35. #include "global.h"
  36. #include "ztype.h"
  37. #include "alloc.h"
  38.  
  39. #define BASE10    1
  40. #define BASE8    2
  41. #define BASE16    3
  42.  
  43. #define GRANULARITY 256
  44.  
  45. extern char *next_c;
  46.  
  47. static int tok_flags = 0;
  48. static TokenP pushback_list;
  49.  
  50. /* mk_Token() -- allocate and initialize space for a Token */
  51. TokenP mk_Token()
  52. {
  53.   register TokenP T = alloc_Token();
  54.  
  55.   T->val = T->hashval = T->flags = T->type = T->subtype = 0;
  56.   T->_txt.out_of_line = T->_ws.out_of_line = NULL;
  57.   T->flags |= (INLINE_TXT | INLINE_WS);
  58.   T->_txt.inline[0] = T->_ws.inline[0] = '\0';
  59.   T->next = NULL;
  60.   return T;
  61. }
  62.  
  63. /* clear_txt() -- clear the text space of a token */
  64. void clear_txt(T)
  65.   register TokenP T;
  66. {
  67.   if (!(T->flags & INLINE_TXT) && T->_txt.out_of_line)
  68.     free(T->_txt.out_of_line);
  69.   T->flags |= INLINE_TXT;
  70.   T->_txt.inline[0] = '\0';
  71. }
  72.  
  73. /* clear_ws() -- clear the white space of a token */
  74. void clear_ws(T)
  75.   register TokenP T;
  76. {
  77.   if (!(T->flags & INLINE_WS) && T->_ws.out_of_line)
  78.     free(T->_ws.out_of_line);
  79.   T->flags |= INLINE_WS;
  80.   T->_ws.inline[0] = '\0';
  81. }
  82.  
  83. /* set_txt() -- set the text of a token to |s|, copying if necessary,
  84.    and deleting the current text, if any */
  85. void set_txt(T, s)
  86.   register TokenP T;
  87.   register char *s;
  88. {
  89.   if (!(T->flags & INLINE_TXT) && T->_txt.out_of_line)
  90.     free(T->_txt.out_of_line);
  91.   if (strlen(s) <= 7) {
  92.     T->flags |= INLINE_TXT;
  93.     strcpy(T->_txt.inline, s);
  94.   } else {
  95.     T->flags &= ~INLINE_TXT;
  96.     T->_txt.out_of_line = strdup(s);
  97.   }
  98. }
  99.  
  100. /* set_txt_n() -- set the text of a token to the first |n| characters
  101.    of |s|, copying if necessary, and deleting the current text, if any */
  102. void set_txt_n(T, s, n)
  103.   register TokenP T;
  104.   register char *s;
  105.   int n;
  106. {
  107.   if (!(T->flags & INLINE_TXT) && T->_txt.out_of_line)
  108.     free(T->_txt.out_of_line);
  109.   if (n <= 7) {
  110.     T->flags |= INLINE_TXT;
  111.     strncpy(T->_txt.inline, s, n);
  112.     T->_txt.inline[n] = '\0';
  113.   } else {
  114.     T->flags &= ~INLINE_TXT;
  115.     T->_txt.out_of_line = mallok(n + 1);
  116.     strncpy(T->_txt.out_of_line, s, n);
  117.     T->_txt.out_of_line[n] = '\0';
  118.   }
  119. }
  120.  
  121. /* set_ws() -- set the white space of a token to |s|, copying if
  122.    necessary, and deleting the current white space, if any */
  123. void set_ws(T, s)
  124.   register TokenP T;
  125.   register char *s;
  126. {
  127.   if (!(T->flags & INLINE_WS) && T->_ws.out_of_line)
  128.     free(T->_ws.out_of_line);
  129.   if (strlen(s) <= 3) {
  130.     T->flags |= INLINE_WS;
  131.     strcpy(T->_ws.inline, s);
  132.   } else {
  133.     T->flags &= ~INLINE_WS;
  134.     T->_ws.out_of_line = strdup(s);
  135.   }
  136. }
  137.  
  138. /* free_token() -- return an allocated Token to the free list */
  139. void free_token(T)
  140.   register TokenP T;
  141. {
  142.   T->next = NULL;
  143.   free_tlist(T);
  144. }
  145.  
  146. /* free_tlist() -- return a list of Token's to the free list */
  147. void free_tlist(T)
  148.   register TokenP T;
  149. {
  150.   register TokenP T1;
  151.  
  152.   for (T1 = T; T; T = T1) {
  153.     T1 = T->next;
  154.     clear_txt(T);
  155.     clear_ws(T);
  156. #if 0
  157.     T->next = next_free_tok;
  158.     next_free_tok = T;
  159. #else
  160.     dealloc_Token(T);
  161. #endif
  162.   }
  163. }
  164.  
  165. /*
  166.    copy_token() -- return a new Token that is a duplicate of the given token
  167. */
  168. TokenP copy_token(T1)
  169.   register TokenP T1;
  170. {
  171.   register TokenP T2 = mk_Token();
  172.  
  173.   *T2 = *T1;
  174.   if (!(T1->flags & INLINE_WS))
  175.     T2->_ws.out_of_line = strdup(T1->_ws.out_of_line);
  176.   if (!(T1->flags & INLINE_TXT))
  177.     T2->_txt.out_of_line = strdup(T1->_txt.out_of_line);
  178.   T2->next = NULL;
  179.   return T2;
  180. }
  181.  
  182. /* copy_tlist() -- create a duplicate of a list of Token's */
  183. TokenP copy_tlist(T1)
  184.   register TokenP T1;
  185. {
  186.   Token head;
  187.   register TokenP T2 = &head;
  188.  
  189.   for (T2->next = NULL; T1; T1 = T1->next, T2 = T2->next)
  190.     T2->next = copy_token(T1);
  191.   return head.next;
  192. }
  193.  
  194. /* tok_shutdown() -- free all space allocated for Token's */
  195. void tok_shutdown()
  196. {
  197. #ifdef DEBUG        /* explicitly clean up, to check for memory leaks */
  198. #if 0
  199.   register TokenP T, T1;
  200.   register int i;
  201.  
  202.   for (T1 = T = tok_blocks; T; T = T1) {
  203.     T1 = T->next;
  204.     for (i = 1; i < GRANULARITY; i++) {
  205.       if (T[i].flags & IN_USE) {
  206.     fprintf(stderr, "@@@ Token not freed:  ");
  207.     dump_token(&T[i]);
  208.     fputc('\n', stderr);
  209.       }
  210.       clear_txt(&T[i]);
  211.       clear_ws(&T[i]);
  212.     }
  213.     free(T);
  214.   }
  215.   fprintf(stderr, "%d total blocks allocated\n", num_blocks);
  216. #else /* 0 */
  217.   cleanup_Token();
  218. #endif /* 0 */
  219. #endif
  220. }
  221.  
  222. /*
  223.    push_tlist() -- "un-read" the list of Token's |T|; token() will return all
  224.    of these tokens in order before reading another token from the input file
  225. */
  226. void push_tlist(T)
  227.   register TokenP T;
  228. {
  229.   register TokenP t;
  230.  
  231.   if (!T)
  232.     return;
  233.   t = T;
  234.   while (t->next)
  235.     t = t->next;
  236.   t->next = pushback_list;
  237.   pushback_list = T;
  238. }
  239.  
  240. /* mk_eof() -- makes and returns an EOF_ token */
  241. static TokenP mk_eof()
  242. {
  243.   register TokenP T = mk_Token();
  244.  
  245.   T->type = EOF_;
  246.   T->flags |= INLINE_TXT | INLINE_WS;
  247.   T->_ws.inline[0] = T->_txt.inline[0] = '\0';
  248.   return T;
  249. }
  250.  
  251. /*
  252.    mk_stopper() -- makes and returns a STOP token.  See expand_tlist() for
  253.    further information.
  254. */
  255. TokenP mk_stopper()
  256. {
  257.   register TokenP T = mk_Token();
  258.  
  259.   T->type = STOP;
  260.   T->flags |= INLINE_TXT | INLINE_WS;
  261.   T->_ws.inline[0] = T->_txt.inline[0] = '\0';
  262.   return T;
  263. }
  264.  
  265. /*
  266.    mk_unmarker() -- makes and returns a special token that informs the
  267.    tokenizer to unmark the macro text associated with token |T|.  See
  268.    expand() for further information.
  269. */
  270. TokenP mk_unmarker(T)
  271.   register TokenP T;
  272. {
  273.   register TokenP T1 = copy_token(T);
  274.  
  275.   T1->type = UNMARK;
  276.   T->flags |= INLINE_TXT | INLINE_WS;
  277.   T->_ws.inline[0] = T->_txt.inline[0] = '\0';
  278.   return T1;
  279. }
  280.  
  281. /*
  282.    mk_printable() -- makes and returns an untyped token with an
  283.    arbitrary text body, for purposes of printing directly via
  284.    print_token().
  285. */
  286.  
  287. TokenP mk_printable(s)
  288.   const char *s;
  289. {
  290.   register TokenP T = mk_Token();
  291.  
  292.   T->type = DONT_CARE;
  293.   set_ws(T, " ");
  294.   set_txt(T, s);
  295.   return T;
  296. }
  297.  
  298. /* flush_tokenizer() -- discard all Tokens pushed back by push_tlist() */
  299. void flush_tokenizer()
  300. {
  301.   free_tlist(pushback_list);
  302.   pushback_list = NULL;
  303. }
  304.  
  305. /*
  306.    number() -- copies from |s| into the token |T| a string of characters
  307.    denoting an integer or floating-point constant.  Returns a pointer to the
  308.    first uncopied character.
  309. */
  310. static char *number(s, T)
  311.   register char *s;
  312.   register TokenP T;
  313. {
  314.   int numtype = BASE10, fpflag = 0;
  315.   char *t;
  316.  
  317.   T->type = NUMBER;
  318.   if (*s == '0') {
  319.     /* check for octal or hexadecimal constant */
  320.     if ((s[1] == 'x' || s[1] == 'X') && isxdigit(s[2])) {
  321.       numtype = BASE16;
  322.       T->flags |= UNS_VAL;
  323.     } else if (is_octal(s[1])) {
  324.       numtype = BASE8;
  325.       T->flags |= UNS_VAL;
  326.     }
  327.   }
  328.   T->val = strtol(s, &t, 0);
  329.   s = t;
  330.   if (numtype != BASE10 || is_isuff(*s)) {
  331.  
  332.     /*
  333.        if we're not in base 10, or the next characters are integer constant
  334.        suffixes, this can't be a floating-point constant
  335.     */
  336.     while (is_isuff(*s)) {
  337.       if (*s == 'u' || *s == 'U')
  338.     T->flags |= UNS_VAL;
  339.       s++;
  340.     }
  341.     return s;
  342.   }
  343.   /* check to see if the number is actually floating point */
  344.   if (*s == '.') {
  345.     fpflag = 1;
  346.     do
  347.       s++;
  348.     while (isdigit(*s));
  349.   }
  350.   if (*s == 'e' || *s == 'E') {
  351.     register char *u = s;
  352.  
  353.     u++;
  354.     if (*u == '-' || *u == '+')
  355.       u++;
  356.     if (isdigit(*u)) {
  357.       fpflag = 1;
  358.       do
  359.     u++;
  360.       while (isdigit(*u));
  361.       s = u;
  362.     }
  363.   }
  364.   if (fpflag) {
  365.     T->type = FP_NUM;
  366.     if (is_fsuff(*s))
  367.       s++;
  368.   }
  369.   return s;
  370. }
  371.  
  372. /*
  373.    char_constant() -- copy from |s| into the token |T| a string of characters
  374.    denoting a character constant.  We do not translate escape sequences at
  375.    this point, though we might need to
  376. */
  377. static char *char_constant(s, T)
  378.   register char *s;
  379.   register TokenP T;
  380. {
  381.   T->type = CHAR_CON;
  382.   for (; *s; s++) {
  383.     if (*s == '\'')
  384.       return s + 1;
  385.     if (*s == '\\')
  386.       s++;
  387.   }
  388.   error("unterminated character constant");
  389.   return s;
  390. }
  391.  
  392. /*
  393.    string_literal() -- copy from |s| into the token |T| a string of
  394.    characters denoting a string literal.  We do not translate escape
  395.    sequences at this point, though we might need to
  396. */
  397. static char *string_literal(s, T)
  398.   register char *s;
  399.   register TokenP T;
  400. {
  401.   T->type = STR_CON;
  402.   for (; *s; s++) {
  403.     if (*s == '"')
  404.       return s + 1;
  405.     if (*s == '\\')
  406.       s++;
  407.   }
  408.   error("unterminated string literal");
  409.   return s;
  410. }
  411.  
  412. /*
  413.    include_name() -- copy from |s| into the token |T| a string of characters
  414.    denoting an #include file specifier enclosed in <>. |s| points to the
  415.    character after the '>'.
  416. */
  417. static char *include_name(s, T)
  418.   register char *s;
  419.   register TokenP T;
  420. {
  421.   T->type = INC_NAM;
  422.   for (; *s; s++) {
  423.     if (*s == '>')
  424.       return s + 1;
  425.   }
  426.   error("unterminated include file name");
  427. }
  428.  
  429. /* set_mode() -- set the tokenizer flags to |m| */
  430. void set_mode(m)
  431.   int m;
  432. {
  433.   tok_flags = m;
  434. }
  435.  
  436. /*
  437.    change_mode() -- twiddle the tokenizer flags; in particular, set the flags
  438.    specified in |raise| and clear the flags specified in |lower|
  439. */
  440. void change_mode(raise, lower)
  441.   int raise, lower;
  442. {
  443.   tok_flags |= raise;
  444.   tok_flags &= (~lower);
  445. }
  446.  
  447. /* get_mode() -- return the current value of the tokenizer flags */
  448. int get_mode()
  449. {
  450.   return tok_flags;
  451. }
  452.  
  453. /*
  454.    xlate_token() -- determines the type of the next preprocessor token in the
  455.    string pointed to by |s|.  Information about the token found is placed in
  456.    the Token |T|.  Returns a pointer to the first character not in the token
  457.    read.
  458. */
  459. static char *xlate_token(s, T)
  460.   register char *s;
  461.   register TokenP T;
  462. {
  463.   if (is_ctoks(*s)) {
  464.     char *t;
  465.  
  466.     T->hashval = hash_id(s, &t);
  467.     s = t;
  468.     T->type = ID;
  469.     return t;
  470.   } else if (isdigit(*s))
  471.     return number(s, T);
  472.   else
  473.     switch (*s++) {
  474.     case '.':
  475.       T->subtype = '.';
  476.       if (*s == '.' && s[1] == '.') {
  477.     s += 2;
  478.     T->type = DONT_CARE;
  479.       } else if (isdigit(*s))
  480.     s = number(s - 1, T);
  481.       else
  482.     T->type = DONT_CARE;
  483.       break;
  484.     case '#':
  485.       if (*s == '#') {
  486.     s++;
  487.     T->type = TOK_CAT;
  488.       } else
  489.     T->type = POUND;
  490.       break;
  491.     case '&':
  492.       T->subtype = '&';
  493.       if (*s == '&') {
  494.     s++;
  495.     T->type = L_AND_OP;
  496.       } else if (*s == '=') {
  497.     s++;
  498.     T->type = DONT_CARE;
  499.       } else
  500.     T->type = B_AND_OP;
  501.       break;
  502.     case '|':
  503.       T->subtype = '|';
  504.       if (*s == '|') {
  505.     s++;
  506.     T->type = L_OR_OP;
  507.       } else if (*s == '=') {
  508.     s++;
  509.     T->type = DONT_CARE;
  510.       } else
  511.     T->type = B_OR_OP;
  512.       break;
  513.     case '+':
  514.       T->subtype = '+';
  515.       if (*s == s[-1] || *s == '=') {
  516.     s++;
  517.     T->type = DONT_CARE;
  518.       } else
  519.     T->type = ADD_OP;
  520.       break;
  521.     case '~':
  522.       T->type = UNARY_OP;
  523.       T->subtype = '~';
  524.       break;
  525.     case ',':
  526.       T->type = COMMA;
  527.       T->subtype = ',';
  528.       break;
  529.     case '(':
  530.       T->type = LPAREN;
  531.       T->subtype = '(';
  532.       break;
  533.     case ')':
  534.       T->type = RPAREN;
  535.       T->subtype = ')';
  536.       break;
  537.     case '!':
  538.       T->subtype = '!';
  539.       if (*s == '=') {
  540.     s++;
  541.     T->type = EQ_OP;
  542.       } else
  543.     T->type = UNARY_OP;
  544.       break;
  545.     case '=':
  546.       T->subtype = '=';
  547.       if (*s == '=') {
  548.     s++;
  549.     T->type = EQ_OP;
  550.       } else
  551.     T->type = DONT_CARE;
  552.       break;
  553.     case '*':
  554.     case '/':
  555.     case '%':
  556.       T->subtype = s[-1];
  557.       if (*s == '=') {
  558.     s++;
  559.     T->type = DONT_CARE;
  560.       } else
  561.     T->type = MUL_OP;
  562.       break;
  563.     case '^':
  564.       T->subtype = '^';
  565.       if (*s == '=') {
  566.     s++;
  567.     T->type = DONT_CARE;
  568.       } else
  569.     T->type = B_XOR_OP;
  570.       break;
  571.     case '-':
  572.       T->subtype = '-';
  573.       if (*s == '-' || *s == '=' || *s == '>') {
  574.     s++;
  575.     T->type = DONT_CARE;
  576.       } else
  577.     T->type = ADD_OP;
  578.       break;
  579.     case '<':
  580.       if (tok_flags & INCLUDE_LINE) {
  581.     s = include_name(s, T);
  582.     break;
  583.       }
  584.       /* else fall through */
  585.     case '>':
  586.       T->subtype = s[-1];
  587.       T->type = REL_OP;
  588.       if (*s == s[-1]) {
  589.     s++;
  590.     T->type = SHIFT_OP;
  591.       }
  592.       if (*s == '=') {
  593.     s++;
  594.     if (T->type == REL_OP)
  595.       T->subtype = (T->subtype == '<' ? '(' : ')');
  596.     else
  597.       T->type = DONT_CARE;
  598.       }
  599.       break;
  600.     case '\'':
  601.       s = char_constant(s, T);
  602.       break;
  603.     case '"':
  604.       s = string_literal(s, T);
  605.       break;
  606.     case '[':
  607.     case ']':
  608.     case '{':
  609.     case '}':
  610.     case ';':
  611.     case ':':
  612.     case '?':
  613.       T->type = DONT_CARE;
  614.       break;
  615.     default:
  616.       T->type = UNKNOWN;
  617.     }
  618.   return s;
  619. }
  620.  
  621. /* print_token() -- write token |T| to the output file */
  622. void print_token(T)
  623.   register TokenP T;
  624. {
  625.   if (in_config_file)
  626.     return;
  627.   if (T->type == STOP)
  628.     bugchk("STOP token in output stream?");
  629.   fputs(token_ws(T), outf);
  630.   fputs(token_txt(T), outf);
  631.   if (T->flags & TRAIL_SPC)
  632.     fputc(' ', outf);
  633. }
  634.  
  635. /*
  636.    merge_tokens() -- Perform token pasting on Token's |T1| and |T2|. Returns
  637.    the resulting token.
  638. */
  639. TokenP merge_tokens(T1, T2)
  640.   register TokenP T1, T2;
  641. {
  642.   register TokenP T = mk_Token();
  643.   register char *t;
  644.  
  645.   set_ws(T, token_ws(T1));
  646.   t = mallok(strlen(token_txt(T1)) + strlen(token_txt(T2)) + 1);
  647.   strcpy(t, token_txt(T1));
  648.   strcat(t, token_txt(T2));
  649.   set_txt(T, t);
  650.   free(t);
  651.   t = xlate_token(token_txt(T), T);
  652.   if (*t != '\0') {
  653.     if (w_bad_concat)
  654.       warning("Invalid token \"%s\" created by concatenation",
  655.           token_txt(T));
  656.     T->type = UNKNOWN;
  657.   }
  658.   return T;
  659. }
  660.  
  661. TokenP _one_token()
  662. {
  663.   register TokenP T = mk_Token();
  664.   register char *s = next_c, *t, *u;
  665.   int n;
  666.  
  667.   t = suck_ws(s, T);
  668.   if (!t || !*t) {
  669.     set_txt(T, "\n");
  670.     T->type = EOL;
  671.     T->subtype = '\n';
  672.     next_c = t;
  673.     return T;
  674.   }
  675.   u = xlate_token(t, T);
  676.   n = (int)(u - t);
  677.   if (T->type == UNKNOWN && w_bad_chars)
  678.     error("Unrecognized character 0x%02x='%c'", *t, *t);
  679.   set_txt_n(T, t, n);
  680.   next_c = u;
  681.   return T;
  682. }
  683.  
  684. void _tokenize_line()
  685. {
  686.   Token head;
  687.   register TokenP T = &head;
  688.  
  689.   head.next = NULL;
  690.   do {
  691.     T = T->next = _one_token();
  692.   } while (T->type != EOL);
  693.   push_tlist(head.next);
  694. }
  695.  
  696. TokenP token()
  697. {
  698.   register TokenP T;
  699.   register char *s;
  700.  
  701.   while (pushback_list) {
  702.     T = pushback_list;
  703.     pushback_list = T->next;
  704.     T->next = NULL;
  705.     if (T->type == UNMARK) {
  706.       Macro *M;
  707.  
  708.       M = lookup(token_txt(T), T->hashval);
  709.       if (!M)
  710.     bugchk("UNMARK on non-macro token %s", token_txt(T));
  711.       if (!(M->flags & MARKED))
  712.     bugchk("UNMARK on unmarked macro %s", token_txt(T));
  713.       M->flags ^= MARKED;
  714.       free_token(T);
  715.       continue;
  716.     } else {
  717.       return T;
  718.     }
  719.   }
  720.  
  721.   /*
  722.      if we get to here, the pushback list is empty, and we need to read in
  723.      another line
  724.   */
  725.   next_c = s = getline();
  726.   if (!s)
  727.     return mk_eof();
  728.   T = _one_token();
  729.   if (T->type == EOL) {
  730.     return T;
  731.   }
  732.   /* we need preprocessor lines in raw form, so we can't pre-tokenize */
  733.   if (T->type != POUND || get_mode() & SLURP)
  734.     _tokenize_line();
  735.   return T;
  736. }
  737.  
  738. TokenP exp_token()
  739. {
  740.   register TokenP T = token();
  741.   register Macro *M;
  742.  
  743.   if (T->type == ID && !(T->flags & BLUEPAINT) &&
  744.       (M = lookup(token_txt(T), T->hashval))) {
  745.     expand(T, M);
  746.     free_token(T);
  747.     return exp_token();
  748.   } else
  749.     return T;
  750. }
  751.  
  752. #ifdef DEBUG
  753.  
  754. /*
  755.    debugging routines to display tokens in internal format
  756. */
  757.  
  758. void dump_token(T)
  759.   TokenP T;
  760. {
  761.   static char *type_names[] =
  762.   {"<<< ERROR >>>", "UNKNOWN", "DONT_CARE",
  763.    "EOL", "NUMBER", "FP_NUM", "ID", "STR_CON", "CHAR_CON", "UNARY_OP",
  764.    "MUL_OP", "ADD_OP", "SHIFT_OP", "REL_OP", "EQ_OP", "B_AND_OP",
  765.    "B_XOR_OP", "B_OR_OP", "L_AND_OP", "L_OR_OP", "LPAREN", "RPAREN",
  766.    "COMMA", "INC_NAM", "POUND", "TOK_CAT", "MACRO_ARG", "EOF_", "STOP",
  767.    "UNMARK"
  768.   };
  769.  
  770.   fprintf(stderr, "[%s, '%c', \"%s\", \"%s\", %ld, %x]@%p",
  771.       type_names[T->type], (T->subtype ? T->subtype : ' '),
  772.       (token_ws(T) ? token_ws(T) : "(null)"),
  773.       (token_txt(T) ? token_txt(T) : "(null)"),
  774.       T->val, T->flags, (void *)T
  775.       );
  776. }
  777.  
  778. void dump_tlist(T)
  779.   TokenP T;
  780. {
  781.   while (T) {
  782.     dump_token(T);
  783.     fputc('\n', stderr);
  784.     T = T->next;
  785.   }
  786. }
  787.  
  788. void dump_pushback()
  789. {
  790.   dump_tlist(pushback_list);
  791. }
  792.  
  793. #endif /* DEBUG */
  794.